home *** CD-ROM | disk | FTP | other *** search
/ QRZ! Ham Radio 8 / QRZ Ham Radio Callsign Database - Volume 8.iso / mac / files / t_sys5 / unixcpio.gz / unixnet.cpio / ftpserv.c < prev    next >
C/C++ Source or Header  |  1994-07-11  |  18KB  |  759 lines

  1. /* FTP Server state machine - see RFC 959 */
  2.  
  3. #define    LINELEN        128    /* Length of command buffer */
  4.  
  5. #include <stdio.h>
  6. #include <ctype.h>
  7. #include "global.h"
  8. #include "config.h"
  9. #include "mbuf.h"
  10. #include "netuser.h"
  11. #include "timer.h"
  12. #include "tcp.h"
  13. #include "ftp.h"
  14. #include "telnet.h"
  15. #include "iface.h"
  16. #include "ax25.h"
  17. #include "lapb.h"
  18. #include "finger.h"
  19. #include "session.h"
  20. #include "nr4.h"
  21. #ifdef UNIX
  22. #include "unix.h"
  23. #undef    toupper
  24. #undef    tolower
  25. #include <string.h>
  26. #include <sys/types.h>
  27. time_t  time();
  28. #endif /* UNIX */
  29.  
  30. /* Command table */
  31. static char *commands[] = {
  32.     "user",
  33. #define    USER_CMD    0
  34.     "acct",
  35. #define    ACCT_CMD    1
  36.     "pass",
  37. #define    PASS_CMD    2
  38.     "type",
  39. #define    TYPE_CMD    3
  40.     "list",
  41. #define    LIST_CMD    4
  42.     "cwd",
  43. #define    CWD_CMD        5
  44.     "dele",
  45. #define    DELE_CMD    6
  46.     "name",
  47. #define    NAME_CMD    7
  48.     "quit",
  49. #define    QUIT_CMD    8
  50.     "retr",
  51. #define    RETR_CMD    9
  52.     "stor",
  53. #define    STOR_CMD    10
  54.     "port",
  55. #define    PORT_CMD    11
  56.     "nlst",
  57. #define    NLST_CMD    12
  58.     "pwd",
  59. #define    PWD_CMD        13
  60.     "xpwd",            /* For compatibility with 4.2BSD */
  61. #define    XPWD_CMD    14
  62.     "mkd ",
  63. #define    MKD_CMD        15
  64.     "xmkd",            /* For compatibility with 4.2BSD */
  65. #define    XMKD_CMD    16
  66.     "xrmd",            /* For compatibility with 4.2BSD */
  67. #define    XRMD_CMD    17
  68.     "rmd ",
  69. #define    RMD_CMD        18
  70.     "stru",
  71. #define    STRU_CMD    19
  72.     "mode",
  73. #define    MODE_CMD    20
  74.     NULLCHAR
  75. };
  76.  
  77. /* Response messages */
  78. static char banner[] = "220 %s FTP version %s ready at %s\r\n";
  79. static char badcmd[] = "500 Unknown command\r\n";
  80. static char unsupp[] = "500 Unsupported command or option\r\n";
  81. static char givepass[] = "331 Enter PASS command\r\n";
  82. static char logged[] = "230 Logged in\r\n";
  83. static char typeok[] = "200 Type OK\r\n";
  84. static char only8[] = "501 Only logical bytesize 8 supported\r\n";
  85. static char deleok[] = "250 File deleted\r\n";
  86. static char mkdok[] = "200 MKD ok\r\n";
  87. static char delefail[] = "550 Delete failed\r\n";
  88. static char pwdmsg[] = "257 \"%s\" is current directory\r\n";
  89. static char badtype[] = "501 Unknown type \"%s\"\r\n";
  90. static char badport[] = "501 Bad port syntax\r\n";
  91. static char unimp[] = "502 Command not yet implemented\r\n";
  92. static char bye[] = "221 Goodbye!\r\n";
  93. static char nodir[] = "553 Can't read directory \"%s\"\r\n";
  94. static char cantopen[] = "550 Can't read file \"%s\"\r\n";
  95. static char sending[] = "150 Opening data connection for %s %s\r\n";
  96. static char cantmake[] = "553 Can't create \"%s\"\r\n";
  97. static char portok[] = "200 Port command okay\r\n";
  98. static char rxok[] = "226 File received OK\r\n";
  99. static char txok[] = "226 File sent OK\r\n";
  100. static char noperm[] = "550 Permission denied\r\n";
  101. static char noconn[] = "425 Data connection reset\r\n";
  102. static char notlog[] = "530 Please log in with USER and PASS\r\n";
  103. static char okay[] = "200 Ok\r\n";
  104.  
  105. static struct tcb *ftp_tcb;
  106.  
  107. /* Start up FTP service */
  108. ftp1(argc,argv)
  109. int argc;
  110. char *argv[];
  111. {
  112.     struct socket lsocket;
  113.     void ftpscr(),ftpscs();
  114.  
  115.     lsocket.address = ip_addr;
  116.     if(argc < 2)
  117.         lsocket.port = FTP_PORT;
  118.     else
  119.         lsocket.port = atoi(argv[1]);
  120.  
  121.     ftp_tcb = open_tcp(&lsocket,NULLSOCK,TCP_SERVER,0,ftpscr,NULLVFP,ftpscs,0,(char *)NULL);
  122. }
  123. /* Shut down FTP server */
  124. ftp0()
  125. {
  126.     if(ftp_tcb != NULLTCB)
  127.         close_tcp(ftp_tcb);
  128. }
  129. /* FTP Server Control channel State change upcall handler */
  130. /*ARGSUSED*/
  131. static
  132. void
  133. ftpscs(tcb,old,new)
  134. struct tcb *tcb;
  135. char old,new;
  136. {
  137.     extern char hostname[],version[];
  138.     struct ftp *ftp,*ftp_create();
  139.     void ftp_delete();
  140.     char *inet_ntoa();
  141.     long t;
  142.     char *cp,*cp1;
  143.  
  144.     switch(new){
  145. /* Setting QUICKSTART piggybacks the server's banner on the SYN/ACK segment;
  146.  * leaving it unset waits for the three-way handshake to complete before
  147.  * sending the banner. Piggybacking unfortunately breaks some old TCPs,
  148.  * so its use is not (yet) recommended.
  149. */
  150. #ifdef    QUICKSTART
  151.     case SYN_RECEIVED:
  152. #else
  153.     case ESTABLISHED:
  154. #endif
  155.         if((ftp = ftp_create(LINELEN)) == NULLFTP){
  156.             /* No space, kill connection */
  157.             close_tcp(tcb);
  158.             return;
  159.         }
  160.         ftp->control = tcb;        /* Downward link */
  161.         tcb->user = (char *)ftp;    /* Upward link */
  162.  
  163.         /* Set default data port */
  164.         ftp->port.address = tcb->conn.remote.address;
  165.         ftp->port.port = FTPD_PORT;
  166.  
  167.         /* Note current directory */
  168.         log(tcb,"open FTP");
  169.         time(&t);
  170.         cp = ctime(&t);
  171.         if((cp1 = index(cp,'\n')) != NULLCHAR)
  172.             *cp1 = '\0';
  173.         tprintf(ftp->control,banner,hostname,version,cp);
  174.         break;        
  175.     case CLOSE_WAIT:
  176.         close_tcp(tcb);
  177.         break;
  178.     case CLOSED:
  179.         log(tcb,"close FTP");
  180.         if((ftp = (struct ftp *)tcb->user) != NULLFTP ||
  181.            !tcpval(ftp->control)) {   /* control session existing ? */
  182.             if (ftp != NULLFTP)
  183.                 ftp_delete(ftp);
  184.         }
  185.         /* Check if server is being shut down */
  186.         if(tcb == ftp_tcb)
  187.             ftp_tcb = NULLTCB;
  188.         del_tcp(tcb);
  189.         break;
  190.     }
  191. }
  192.  
  193. /* FTP Server Control channel Receiver upcall handler */
  194. /*ARGSUSED*/
  195. static
  196. void
  197. ftpscr(tcb,cnt)
  198. struct tcb *tcb;
  199. int16 cnt;
  200. {
  201.     register struct ftp *ftp;
  202.     char c;
  203.     struct mbuf *bp;
  204.     void ftpcommand();
  205.  
  206.     if((ftp = (struct ftp *)tcb->user) == NULLFTP){
  207.         /* Unknown connection, just kill it */
  208.         close_tcp(tcb);
  209.         return;
  210.     }
  211.     switch(ftp->state){
  212.     case COMMAND_STATE:
  213.         /* Assemble an input line in the session buffer. Return if incomplete */
  214.         recv_tcp(tcb,&bp,0);
  215.         while(pullup(&bp,&c,1) == 1){
  216.             switch(c){
  217.             case '\r':    /* Strip cr's */
  218.                 continue;
  219.             case '\n':    /* Complete line; process it */
  220.                 ftp->buf[ftp->cnt] = '\0';
  221.                 ftpcommand(ftp);
  222.                 ftp->cnt = 0;
  223.                 break;
  224.             default:    /* Assemble line */
  225.                 if(ftp->cnt != LINELEN-1)
  226.                     ftp->buf[ftp->cnt++] = c;
  227.                 break;
  228.             }
  229.         }
  230.         /* else no linefeed present yet to terminate command */
  231.         break;
  232.     case SENDING_STATE:
  233.     case RECEIVING_STATE:
  234.         /* Leave commands pending on receive queue until
  235.          * present command is done
  236.          */
  237.         break;
  238.     }
  239. }
  240.  
  241. /* FTP server data channel connection state change upcall handler */
  242. void
  243. ftpsds(tcb,old,new)
  244. struct tcb *tcb;
  245. char old,new;
  246. {
  247.     register struct ftp *ftp;
  248.  
  249.     if((ftp = (struct ftp *)tcb->user) == NULLFTP){
  250.         /* Unknown connection. Kill it */
  251.         del_tcp(tcb);
  252.     } else if((old == FINWAIT1 || old == CLOSING) && ftp->state == SENDING_STATE){
  253.         /* We've received an ack of our FIN while sending; we're done */
  254.         ftp->state = COMMAND_STATE;
  255.         tprintf(ftp->control,txok);
  256.         /* Kick command parser if something is waiting */
  257.         if(ftp->control->rcvcnt != 0)
  258.             ftpscr(ftp->control,ftp->control->rcvcnt);
  259.     } else if(ftp->state == RECEIVING_STATE && new == CLOSE_WAIT){
  260.         /* FIN received on incoming file */
  261. #ifdef    CPM
  262.         if(ftp->type == ASCII_TYPE)
  263.             putc(CTLZ,ftp->fp);
  264. #endif
  265.         close_tcp(tcb);
  266.         if(ftp->fp != stdout)
  267.             fclose(ftp->fp);
  268.         ftp->fp = NULLFILE;
  269.         ftp->state = COMMAND_STATE;
  270.         tprintf(ftp->control,rxok);
  271.         /* Kick command parser if something is waiting */
  272.         if(ftp->control->rcvcnt != 0)
  273.             ftpscr(ftp->control,ftp->control->rcvcnt);
  274.     } else if(new == CLOSED){
  275.         if(tcb->reason != NORMAL){
  276.             /* Data connection was reset, complain about it */
  277.             tprintf(ftp->control,noconn);
  278.             /* And clean up */
  279.             if(ftp->fp != NULLFILE && ftp->fp != stdout)
  280.                 fclose(ftp->fp);
  281.             ftp->fp = NULLFILE;
  282.             ftp->state = COMMAND_STATE;
  283.             /* Kick command parser if something is waiting */
  284.             if(ftp->control->rcvcnt != 0)
  285.                 ftpscr(ftp->control,ftp->control->rcvcnt);
  286.         }
  287.         /* Clear only if another transfer hasn't already started */
  288.         if(ftp->data == tcb)
  289.             ftp->data = NULLTCB;
  290.         del_tcp(tcb);
  291.     }
  292. }
  293.  
  294. /* Parse and execute ftp commands */
  295. static
  296. void
  297. ftpcommand(ftp)
  298. register struct ftp *ftp;
  299. {
  300.     void ftpdr(),ftpdt(),ftpsds();
  301.     char *cmd,*arg,*cp,**cmdp,*file;
  302.     char *pathname();
  303.     char *mode;
  304.     struct socket dport;
  305.     int i;
  306.  
  307. #ifndef    CPM
  308.     FILE *dir();
  309. #endif
  310.  
  311.     cmd = ftp->buf;
  312.     if(ftp->cnt == 0){
  313.         /* Can't be a legal FTP command */
  314.         tprintf(ftp->control,badcmd);
  315.         return;
  316.     }    
  317.     cmd = ftp->buf;
  318.  
  319. #ifdef    UNIX
  320.     /* Translate first word to lower case */
  321.     for(cp = cmd;*cp != ' ' && *cp != '\0';cp++)
  322.         *cp = tolower(*cp);
  323. #else
  324.     /* Translate entire buffer to lower case */
  325.     for(cp = cmd;*cp != '\0';cp++)
  326.         *cp = tolower(*cp);
  327. #endif
  328.     /* Find command in table; if not present, return syntax error */
  329.     for(cmdp = commands;*cmdp != NULLCHAR;cmdp++)
  330.         if(strncmp(*cmdp,cmd,strlen(*cmdp)) == 0)
  331.             break;
  332.     if(*cmdp == NULLCHAR){
  333.         tprintf(ftp->control,badcmd);
  334.         return;
  335.     }
  336.     /* Allow only USER, PASS and QUIT before logging in */
  337.     if(ftp->cd == NULLCHAR || ftp->path[0] == NULLCHAR){
  338.         switch(cmdp-commands){
  339.         case USER_CMD:
  340.         case PASS_CMD:
  341.         case QUIT_CMD:
  342.             break;
  343.         default:
  344.             tprintf(ftp->control,notlog);
  345.             return;
  346.         }
  347.     }
  348.     arg = &cmd[strlen(*cmdp)];
  349.     while(*arg == ' ')
  350.         arg++;
  351.  
  352.     /* Execute specific command */
  353.     switch(cmdp-commands){
  354.     case USER_CMD:
  355.         if((ftp->username = malloc((unsigned)strlen(arg)+1)) == NULLCHAR){
  356.             close_tcp(ftp->control);
  357.             break;
  358.         }
  359.         strcpy(ftp->username,arg);
  360.         tprintf(ftp->control,givepass);
  361.          /* erase all user info from possible previous session */
  362.          for(i = 0; i < MAXPATH; i++){
  363.              if(ftp->path[i] != NULLCHAR){
  364.                  free(ftp->path[i]);
  365.                  ftp->path[i] = NULLCHAR;
  366.              }
  367.              ftp->perms[i] = 0;
  368.          }
  369.          if(ftp->cd != NULLCHAR){
  370.              free(ftp->cd);
  371.              ftp->cd = NULLCHAR;
  372.          }
  373.         break;
  374.     case TYPE_CMD:
  375.         switch(arg[0]){
  376.         case 'A':
  377.         case 'a':    /* Ascii */
  378.             ftp->type = ASCII_TYPE;
  379.             tprintf(ftp->control,typeok);
  380.             break;
  381.         case 'l':
  382.         case 'L':
  383.             while(*arg != ' ' && *arg != '\0')
  384.                 arg++;
  385.             if(*arg == '\0' || *++arg != '8'){
  386.                 tprintf(ftp->control,only8);
  387.                 break;
  388.             }    /* Note fall-thru */
  389.         case 'B':
  390.         case 'b':    /* Binary */
  391.         case 'I':
  392.         case 'i':    /* Image */
  393.             ftp->type = IMAGE_TYPE;
  394.             tprintf(ftp->control,typeok);
  395.             break;
  396.         default:    /* Invalid */
  397.             tprintf(ftp->control,badtype,arg);
  398.             break;
  399.         }
  400.         break;
  401.     case QUIT_CMD:
  402.         tprintf(ftp->control,bye);
  403.         close_tcp(ftp->control);
  404.         break;
  405.     case RETR_CMD:
  406.         /* Disk operation; return ACK now */
  407.         tcp_output(ftp->control);
  408.         file = pathname(ftp->cd,arg);
  409.         if(ftp->type == IMAGE_TYPE)
  410.             mode = binmode[READ_BINARY];
  411.         else
  412.             mode = "r";
  413.         if(!permcheck(ftp,RETR_CMD,file)){
  414.              tprintf(ftp->control,noperm);
  415.         } else if((ftp->fp = fopen(file,mode)) == NULLFILE){
  416.             tprintf(ftp->control,cantopen,file);
  417.         } else {
  418.             log(ftp->control,"RETR %s",file);
  419.             dport.address = ip_addr;
  420.             dport.port = FTPD_PORT;
  421.             ftp->state = SENDING_STATE;
  422.             tprintf(ftp->control,sending,"RETR",arg);
  423.             ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE,
  424.              0,NULLVFP,ftpdt,ftpsds,ftp->control->tos,(char *)ftp);
  425.         }
  426.         free(file);
  427.         break;
  428.     case STOR_CMD:
  429.         /* Disk operation; return ACK now */
  430.         tcp_output(ftp->control);
  431.         file = pathname(ftp->cd,arg);
  432.         if(ftp->type == IMAGE_TYPE)
  433.             mode = binmode[WRITE_BINARY];
  434.         else
  435.             mode = "w";
  436.         if(!permcheck(ftp,STOR_CMD,file)){
  437.              tprintf(ftp->control,noperm);
  438.             free(file);
  439.              break;
  440.         } else if((ftp->fp = fopen(file,mode)) == NULLFILE){
  441.             tprintf(ftp->control,cantmake,file);
  442.         } else {
  443.             log(ftp->control,"STOR %s",file);
  444.             dport.address = ip_addr;
  445.             dport.port = FTPD_PORT;
  446.             ftp->state = RECEIVING_STATE;
  447.             tprintf(ftp->control,sending,"STOR",arg);
  448.             ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE,
  449.              0,ftpdr,NULLVFP,ftpsds,ftp->control->tos,(char *)ftp);
  450.         }
  451.         free(file);
  452.         break;
  453.     case PORT_CMD:
  454.         if(pport(&ftp->port,arg) == -1){
  455.             tprintf(ftp->control,badport);
  456.         } else {
  457.             tprintf(ftp->control,portok);
  458.         }
  459.         break;
  460. #ifndef CPM
  461.     case LIST_CMD:
  462.         /* Disk operation; return ACK now */
  463.         tcp_output(ftp->control);
  464.  
  465.         file = pathname(ftp->cd,arg);
  466.         if(!permcheck(ftp,RETR_CMD,file)){
  467.              tprintf(ftp->control,noperm);
  468.         } else if((ftp->fp = dir(file,1)) == NULLFILE){
  469.             tprintf(ftp->control,nodir,file);
  470.         } else {
  471.             dport.address = ip_addr;
  472.             dport.port = FTPD_PORT;
  473.             ftp->state = SENDING_STATE;
  474.             tprintf(ftp->control,sending,"LIST",file);
  475.             ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE,
  476.              0,NULLVFP,ftpdt,ftpsds,ftp->control->tos,(char *)ftp);
  477.         }
  478.         free(file);
  479.         break;
  480.     case NLST_CMD:
  481.         /* Disk operation; return ACK now */
  482.         tcp_output(ftp->control);
  483.  
  484.         file = pathname(ftp->cd,arg);
  485.         if(!permcheck(ftp,RETR_CMD,file)){
  486.              tprintf(ftp->control,noperm);
  487.         } else if((ftp->fp = dir(file,0)) == NULLFILE){
  488.             tprintf(ftp->control,nodir,file);
  489.         } else {
  490.             dport.address = ip_addr;
  491.             dport.port = FTPD_PORT;
  492.             ftp->state = SENDING_STATE;
  493.             tprintf(ftp->control,sending,"NLST",file);
  494.             ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE,
  495.              0,NULLVFP,ftpdt,ftpsds,ftp->control->tos,(char *)ftp);
  496.         }
  497.         free(file);
  498.         break;
  499.     case CWD_CMD:
  500.         tcp_output(ftp->control);    /* Disk operation; return ACK now */
  501.  
  502.         file = pathname(ftp->cd,arg);
  503.         if(!permcheck(ftp,RETR_CMD,file)){
  504.              tprintf(ftp->control,noperm);
  505.             free(file);
  506. #if      (defined(MSDOS) || defined(ATARI_ST))
  507.         /* Don'tcha just LOVE %%$#@!! MS-DOS? */
  508.         } else if(strcmp(file,"\\") == 0 || access(file,0) == 0){
  509. #else
  510.         } else if(access(file,0) == 0){    /* See if it exists */
  511. #endif
  512.             /* Succeeded, record in control block */
  513.             free(ftp->cd);
  514.             ftp->cd = file;
  515.             tprintf(ftp->control,pwdmsg,file);
  516.         } else {
  517.             /* Failed, don't change anything */
  518.             tprintf(ftp->control,nodir,file);
  519.             free(file);
  520.         }
  521.         break;
  522.     case XPWD_CMD:
  523.     case PWD_CMD:
  524.         tprintf(ftp->control,pwdmsg,ftp->cd);
  525.         break;
  526. #else
  527.     case LIST_CMD:
  528.     case NLST_CMD:
  529.     case CWD_CMD:
  530.     case XPWD_CMD:
  531.     case PWD_CMD:
  532. #endif
  533.     case ACCT_CMD:        
  534.         tprintf(ftp->control,unimp);
  535.         break;
  536.     case DELE_CMD:
  537.         file = pathname(ftp->cd,arg);
  538.         if(!permcheck(ftp,DELE_CMD,file)){
  539.              tprintf(ftp->control,noperm);
  540.         } else if(unlink(file) == 0){
  541.             tprintf(ftp->control,deleok);
  542.         } else {
  543.             tprintf(ftp->control,delefail);
  544.         }
  545.         free(file);
  546.         break;
  547.     case PASS_CMD:
  548.         tcp_output(ftp->control);    /* Send the ack now */
  549.         ftplogin(ftp,arg);            
  550.         break;
  551. #ifndef    CPM
  552.     case XMKD_CMD:
  553.     case MKD_CMD:
  554.         file = pathname(ftp->cd,arg);
  555.         if(!permcheck(ftp,MKD_CMD,file)){
  556.             tprintf(ftp->control,noperm);
  557.         } else if(mkdir(file,0777) == 0){
  558.             tprintf(ftp->control,mkdok);
  559.         } else {
  560.             tprintf(ftp->control,cantmake);
  561.         }
  562.         free(file);
  563.         break;
  564.     case XRMD_CMD:
  565.     case RMD_CMD:
  566.         file = pathname(ftp->cd,arg);
  567.         if(!permcheck(ftp,RMD_CMD,file)){
  568.              tprintf(ftp->control,noperm);
  569.         } else if(rmdir(file) == 0){
  570.             tprintf(ftp->control,deleok);
  571.         } else {
  572.             tprintf(ftp->control,delefail);
  573.         }
  574.         free(file);
  575.         break;
  576.     case STRU_CMD:
  577.         if(tolower(arg[0]) != 'f')
  578.             tprintf(ftp->control,unsupp);
  579.         else
  580.             tprintf(ftp->control,okay);
  581.         break;
  582.     case MODE_CMD:
  583.         if(tolower(arg[0]) != 's')
  584.             tprintf(ftp->control,unsupp);
  585.         else
  586.             tprintf(ftp->control,okay);
  587.         break;
  588.     }
  589. #endif
  590. }
  591. static
  592. int
  593. pport(sock,arg)
  594. struct socket *sock;
  595. char *arg;
  596. {
  597.     int32 n;
  598.     int atoi(),i;
  599.  
  600.     n = 0;
  601.     for(i=0;i<4;i++){
  602.         n = atoi(arg) + (n << 8);
  603.         if((arg = index(arg,',')) == NULLCHAR)
  604.             return -1;
  605.         arg++;
  606.     }
  607.     sock->address = n;
  608.     n = atoi(arg);
  609.     if((arg = index(arg,',')) == NULLCHAR)
  610.         return -1;
  611.     arg++;
  612.     n = atoi(arg) + (n << 8);
  613.     sock->port = n;
  614.     return 0;
  615. }
  616. /* Attempt to log in the user whose name is in ftp->username and password
  617.  * in pass
  618.  */
  619. static
  620. ftplogin(ftp,pass)
  621. struct ftp *ftp;
  622. char *pass;
  623. {
  624.     char buf[80],*cp,*cp1,*getnenv();
  625.     FILE *fp;
  626.     int anony = 0;
  627.     int i;
  628.  
  629.     if((fp = fopen(userfile,"r")) == NULLFILE){
  630.         /* Userfile doesn't exist */
  631.         tprintf(ftp->control,noperm);
  632.         return;
  633.     }
  634.  
  635.     while(fgets(buf,sizeof(buf),fp),!feof(fp)){
  636.         if(buf[0] == '#')
  637.             continue;    /* Comment */
  638.         if((cp = index(buf,' ')) == NULLCHAR)
  639.             /* Bogus entry */
  640.             continue;
  641.         *cp++ = '\0';        /* Now points to password */
  642.         if(strcmp(ftp->username,buf) == 0)
  643.             break;        /* Found user name */
  644.     }
  645.     if(feof(fp)){
  646.         /* User name not found in file */
  647.         fclose(fp);
  648.         tprintf(ftp->control,noperm);
  649.         return;
  650.     }
  651.     fclose(fp);
  652.     /* Look for space after password field in file */
  653.     if((cp1 = index(cp,' ')) == NULLCHAR){
  654.         /* Invalid file entry */
  655.         tprintf(ftp->control,noperm);
  656.         return;
  657.     }
  658.     *cp1++ = '\0';    /* Now points to first path field */
  659.     if(strcmp(cp,"*") == 0)
  660.         anony = 1;    /* User ID is password-free */
  661.     if(!anony && strcmp(cp,pass) != 0){
  662.         /* Password required, but wrong one given */
  663.         tprintf(ftp->control,noperm);
  664.         return;
  665.     }
  666.     for(i = 0; i< MAXPATH; i++){
  667.       if((cp = index(cp1,' ')) == NULLCHAR){
  668.         /* Permission field missing, assume end of line */
  669.         break;
  670.       }
  671.       *cp++ = '\0';    /* now points to permission field */
  672.       ftp->path[i] = malloc((unsigned)strlen(cp1)+1);
  673.       strcpy(ftp->path[i],cp1);
  674.       /* set the permission bits */
  675.       ftp->perms[i] = atoi(cp);
  676.       if ((cp1 = index(cp,' ')) == NULLCHAR){
  677.         /* no next path field, so assume end of line */
  678.         break;
  679.       }
  680.       *cp1++ = '\0'; /* cp1 now points to the next path field */
  681.     }
  682.  
  683.     /* Set up current directory and LAST specified path prefix */
  684.     for (i= MAXPATH - 1; i>=0; i--)
  685.       if (ftp->perms[i])
  686.         break;
  687.  
  688.     ftp->cd = malloc((unsigned)strlen(ftp->path[i])+1);
  689.     strcpy(ftp->cd,ftp->path[i]);
  690.     
  691.     tprintf(ftp->control,logged);
  692.     if(!anony)
  693.         log(ftp->control,"%s logged in",ftp->username);
  694.     else
  695.         log(ftp->control,"%s logged in, ID %s",ftp->username,pass);
  696. }        
  697.  
  698. #if    (defined(MSDOS) || defined(ATARI_ST))
  699. /* Illegal characters in a DOS filename */
  700. char badchars[] = "\"[]|<>+=;,";
  701. #endif
  702.  
  703. /* Return 1 if the file operation is allowed, 0 otherwise */
  704. permcheck(ftp,op,file)
  705. struct ftp *ftp;
  706. int op;
  707. char *file;
  708. {
  709. #if    (defined(MSDOS) || defined(ATARI_ST))
  710.     char *cp;
  711. #endif
  712.     int i;
  713.  
  714.     if(file == NULLCHAR || ftp->path[0] == NULLCHAR)
  715.         return 0;    /* Probably hasn't logged in yet */
  716. #if    (defined(MSDOS) || defined(ATARI_ST))
  717.     /* Check for characters illegal in MS-DOS file names */
  718.     for(cp = badchars;*cp != '\0';cp++){
  719.         if(index(file,*cp) != NULLCHAR)
  720.             return 0;    
  721.     }
  722. #endif
  723. #if    (!AMIGA && !MAC)
  724.     /* The target file must be under the user's allowed search path */
  725.      for(i = 0; i < MAXPATH; i++)
  726.          if(ftp->path[i] != NULLCHAR &&
  727.             strncmp(file,ftp->path[i],strlen(ftp->path[i])) == 0)
  728.              break;
  729.  
  730.     if(i == MAXPATH)
  731.         return 0;
  732. #endif
  733.  
  734.     switch(op){
  735.     case RETR_CMD:
  736.         /* User must have permission to read files */
  737.         if(ftp->perms[i] & FTP_READ)
  738.             return 1;
  739.         return 0;
  740.     case DELE_CMD:
  741.     case RMD_CMD:
  742.         /* User must have permission to (over)write files */
  743.         if(ftp->perms[i] & FTP_WRITE)
  744.             return 1;
  745.         return 0;
  746.     case STOR_CMD:
  747.     case MKD_CMD:
  748.         /* User must have permission to (over)write files, or permission
  749.          * to create them if the file doesn't already exist
  750.          */
  751.         if(ftp->perms[i] & FTP_WRITE)
  752.             return 1;
  753.         if(access(file,2) == -1 && (ftp->perms[i] & FTP_CREATE))
  754.             return 1;
  755.         return 0;
  756.     }
  757.     return 0;    /* "can't happen" -- keep lint happy */
  758. }
  759.